package es.uned.etsii.si3.taller.servlets;

import java.sql.*;
import java.util.Map;
import java.util.List;
import java.math.BigDecimal;
import java.text.*;

import org.apache.struts2.interceptor.SessionAware;

import com.opensymphony.xwork2.ActionSupport;

import com.j256.ormlite.stmt.QueryBuilder;
import com.j256.ormlite.stmt.Where;

import es.uned.etsii.si3.taller.*;
import es.uned.etsii.si3.taller.misc.ProporcionaFuncionalidad;

public final class Mecanicos extends ActionSupport implements SessionAware {
	private static final long serialVersionUID = 1L;
	private Map<String, Object> userSession;

	private List<Trabajo> trabajosPendientes;			// Listado obtenido de trabajos sin recepcionar

	private int trabajoSeleccionado = 0;				// id del trabajo seleccionado
	private Trabajo datosTrabajoSeleccionado = null; 
	private int trabajoRecepcionado = 0;				// id del trabajo seleccionado en la pantalla de recepción
	private Trabajo datosTrabajoRecepcionado = null;
	private String fechaRecepcionTrabajo = null;		// Fecha de recepción del trabajo
	private String fechaEstimadaTrabajo = null;			// Fecha estimada de finalización del trabajo
	private String fechaFinTrabajo = null;				// Fecha de finalización del trabajo
	private int trabajoGestionado = 0;					// id del trabajo seleccionado en la pantalla de gestión
	private List<Trabajo> trabajosAsignados = null;		// Todos los trabajos asignados al usuario
	private BigDecimal costeHora = new BigDecimal (0);	// coste por hora del taller
	private List<Material> tipoMateriales = null;		// Contiene los distintos tipos de materiales
	private List<MaterialTrabajo> materialesUsados = null; 	// Contiene los materiales facturados en el trabajo
	private String horasFacturadasTrabajo = null;		// Contiene las horas facturadas actualmente
	private String horasTrabajo = null;					// Contiene las horas que se van a facturar
	private String unidadesMaterial = null;				// Contiene las unidades de un material a facturar
	private int seleccionMaterial = 0;					// Contiene el índice del material seleccionado
	private int materialUsado = 0;						// id del material que queremos modificar
	private MaterialTrabajo materialModificado = null;	// Material a modificar
	
	private String res = null;							// Resultado que devuelve la acción
	
// Getters and setters
	
	public List<Trabajo> getTrabajosPendientes() {
		return trabajosPendientes;
	}
 
	public void setTrabajosPendientes(List<Trabajo> trabajosPendientes) {
		this.trabajosPendientes = trabajosPendientes;
	}

	public int getTrabajoSeleccionado(){
		return trabajoSeleccionado;
	}
	
	public void setTrabajoSeleccionado(int trabajoSeleccionado) {
		this.trabajoSeleccionado = trabajoSeleccionado;
	}
	
	public Trabajo getDatosTrabajoSeleccionado() {
		return datosTrabajoSeleccionado;
	}
	
	public void setDatosTrabajoSeleccionado (Trabajo datosTrabajoSeleccionado) {
		this.datosTrabajoSeleccionado = datosTrabajoSeleccionado;
	}

	public String getFechaRecepcionTrabajo() {
		return fechaRecepcionTrabajo;
	}
	
	public void setFechaRecepcionTrabajo(String fechaRecepcionTrabajo){
		this.fechaRecepcionTrabajo = fechaRecepcionTrabajo;
	}
	
	public String getFechaEstimadaTrabajo() {
		return fechaEstimadaTrabajo;
	}
	
	public void setFechaEstimadaTrabajo(String fechaEstimadaTrabajo){
		this.fechaEstimadaTrabajo = fechaEstimadaTrabajo;
	}
	
	public int getTrabajoRecepcionado() {
		return trabajoRecepcionado;
	}
	
	public void setTrabajoRecepcionado(int trabajoRecepcionado) {
		this.trabajoRecepcionado = trabajoRecepcionado;
	}
	
	public Trabajo getDatosTrabajoRecepcionado() {
		return datosTrabajoRecepcionado;
	}
	
	public void setDatosTrabajoRecepcionado (Trabajo datosTrabajoRecepcionado) {
		this.datosTrabajoRecepcionado = datosTrabajoRecepcionado;
	}
	
	public int getTrabajoGestionado() {
		return trabajoGestionado;
	}
	
	public void setTrabajoGestionado(int trabajoGestionado) {
		this.trabajoGestionado = trabajoGestionado;
	}

	public List<Trabajo> getTrabajosAsignados() {
		return trabajosAsignados;
	}
	
	public void setTrabajosAsignados (List<Trabajo> trabajosAsignados) {
		this.trabajosAsignados =  trabajosAsignados;
	}
	
	public List<Material> getTipoMateriales() {
		return tipoMateriales;
	}
	
	public void setTipoMateriales(List<Material> tipoMateriales) {
		this.tipoMateriales =  tipoMateriales;
	}
	
	public BigDecimal getCosteHora() {
		return costeHora;
	}
	
	public void setCosteHora (BigDecimal costeHora) {
		this.costeHora = costeHora;
	}
	
	public List<MaterialTrabajo> getMaterialesUsados () {
		return materialesUsados;
	}
	
	public void setMaterialesUsados (List<MaterialTrabajo> materialesUsados) {
		this.materialesUsados = materialesUsados;
	}

	public String getFechaFinTrabajo() {
		return fechaFinTrabajo;
	}
	
	public void setFechaFinTrabajo(String fechaFinTrabajo){
		this.fechaFinTrabajo = fechaFinTrabajo;
	}

	public String getHorasFacturadasTrabajo () {
		return horasFacturadasTrabajo;
	}
	
	public void setHorasFacturadasTrabajo (String horasFacturadasTrabajo) {
		this.horasFacturadasTrabajo = horasFacturadasTrabajo;
	}
	
	public String getHorasTrabajo () {
		return horasTrabajo;
	}
	
	public void setHorasTrabajo (String horasTrabajo) {
		this.horasTrabajo = horasTrabajo;
	}

	public String getUnidadesMaterial() {
		return unidadesMaterial;
	}
	
	public void setUnidadesMaterial(String unidadesMaterial) {
		this.unidadesMaterial = unidadesMaterial;
	}

	public int getSeleccionMaterial() {
		return seleccionMaterial;
	}
	
	public void setSeleccionMaterial(int seleccionMaterial) {
		this.seleccionMaterial = seleccionMaterial;
	}

	public int getMaterialUsado() {
		return materialUsado;
	}
	
	public void setMaterialUsado(int materialUsado) {
		this.materialUsado = materialUsado;
	}
	
	public MaterialTrabajo getMaterialModificado () {
		return materialModificado;
	}
	
	public void setMaterialModificado (MaterialTrabajo materialModificado) {
		this.materialModificado = materialModificado;
	}
	
	public Mecanicos(){
		super();
	}

    /**
     * Muestra una lista de trabajos asignados al mecánico pero que aun no 
     * han sido recepcionados
     * Es decir, que el campo fechaRecepcion sea null
     * 
     * @return actionerror
     */
	@ProporcionaFuncionalidad(identificador = FactoriaFunciones.MECANICO_RECEPCIONAR_TRABAJO)
	public String recepcionar() {
		res = new String (SUCCESS);		

		try {
			// Crea un QueryBuilder 
			QueryBuilder<Trabajo, Integer> trabajoQb = 
					FactoriaDAO.getDao(Trabajo.class).queryBuilder(); 
			
			Where<Trabajo, Integer> whereTrabajoQb = trabajoQb.where();
			// Seleccionamos sólo trabajos asignados al mecánico
			Usuario usr = (Usuario)userSession.get("usuario");
			whereTrabajoQb.eq(Trabajo.ID_USUARIO_FIELD_NAME, usr);
			// Que no estén recepcionados
			whereTrabajoQb.and().isNull(Trabajo.FECHARECEPCION_FIELD_NAME);
			
			trabajosPendientes = whereTrabajoQb.query();
		} catch (SQLException e) {
			addActionError("Error al buscar en la BD: " + e);
			res = ERROR;
		}				

		return res;
	}

	/**
	 * Muestra en pantalla los datos del trabajo seleccionado
	 * @return  actionerror
	 */
	@ProporcionaFuncionalidad(identificador = FactoriaFunciones.MECANICO_RECEPCIONAR_TRABAJO)
	public String detallesRecepcion() {
		res = new String (SUCCESS);		

		try {
			datosTrabajoSeleccionado = FactoriaDAO.recuperar(Trabajo.class, trabajoSeleccionado);
		}
		catch (SQLException e) {
			addActionError("Error al recuperar el trabajo de la Base de Datos " + e.toString());
			res = INPUT;
		}
		
		return res;
	}

	/**
	 *  Recepciona el trabajo, grabandole la fecha de entrada y la fecha estimada 
	 *  de finalización
	 * @return  actionerror
	 */
	@ProporcionaFuncionalidad(identificador = FactoriaFunciones.MECANICO_RECEPCIONAR_TRABAJO)
	public String aceptarRecepcion() {
		res = new String (SUCCESS);		

		try {
			SimpleDateFormat df = new SimpleDateFormat("dd/MM/yy"); 

			// Lo volvemos a cargar para que no se pierda
			datosTrabajoSeleccionado = FactoriaDAO.recuperar(Trabajo.class, trabajoRecepcionado);

			// Si la fecha estimada de fin es anterior a la de recepción, damos fallo)
			if (df.parse(fechaRecepcionTrabajo).after(df.parse(fechaEstimadaTrabajo))) {
				addActionError("La fecha estimada de finalización del trabajo no puede ser anterior a la fecha de recepcion del mismo");
				res = INPUT;
			}
			else {
			
				datosTrabajoRecepcionado = FactoriaDAO.recuperar(Trabajo.class, trabajoRecepcionado);
				datosTrabajoRecepcionado.setFechaRecepcion(df.parse(fechaRecepcionTrabajo));
				datosTrabajoRecepcionado.setFechaEstimada(df.parse(fechaEstimadaTrabajo));
				datosTrabajoRecepcionado.setCosteHora(costeHora);
			
				datosTrabajoRecepcionado.update();
				datosTrabajoRecepcionado.refresh();
			}
		}
		catch (SQLException e) {
			addActionError("Error al hacer el update");
			res = INPUT;
		}
		catch (ParseException e) {
			addActionError("Error al comprobar la fecha");			
			res = INPUT;
		} 
		catch (NullPointerException e) {
			addActionError("Error de puntero nulo");
			res = INPUT;
		}
		return res;
	}
	
	/**
	 *  Muestra una lista de trabajos recepcionados por el mecánico
	 * Es decir, que el campo fechaRecepcion no sea null y vistoBueno sea false
	 * return  actionerror
	 */
	@ProporcionaFuncionalidad(identificador = FactoriaFunciones.MECANICO_GESTIONAR_TRABAJO)
	public String trabajar() {
		res = new String (SUCCESS);		

		try {
			// Crea un QueryBuilder 
			QueryBuilder<Trabajo, Integer> trabajoQb = 
					FactoriaDAO.getDao(Trabajo.class).queryBuilder(); 
			
			Where<Trabajo, Integer> whereTrabajoQb = trabajoQb.where();
			// Seleccionamos sólo trabajos asignados al mecánico
			Usuario usr = (Usuario)userSession.get("usuario");
			whereTrabajoQb.eq(Trabajo.ID_USUARIO_FIELD_NAME, usr);
			// Que estén recepcionados
			whereTrabajoQb.and().isNotNull(Trabajo.FECHARECEPCION_FIELD_NAME);
			whereTrabajoQb.and().isNull(Trabajo.FECHAFIN_FIELD_NAME);
			whereTrabajoQb.and().eq(Trabajo.VISTOBUENOJS_FIELD_NAME, false);
			
			trabajosAsignados = whereTrabajoQb.query();
		} catch (SQLException e) {
			addActionError("Error al buscar en la BD: " + e);
			res = ERROR;
		}				

		return res;
	}

	/**
	 *  Muestra en pantalla los datos del trabajo seleccionado para que el mecánico contabilice horas y materiales
	 * o facture el trabajo, pasándolo al JS
	 * return  actionerror
	 */
	@ProporcionaFuncionalidad(identificador = FactoriaFunciones.MECANICO_GESTIONAR_TRABAJO)
	public String detallesSeleccion() {
		res = new String (SUCCESS);		

		try {
// Recuperamos los datos del trabajo seleccionado
			datosTrabajoSeleccionado = FactoriaDAO.recuperar(Trabajo.class, trabajoGestionado);

// Obtenemos la lista de materiales para que el mecánico pueda seleccionar los que desee			
			List<Material> listaMateriales;
			listaMateriales = FactoriaDAO.getDao(Material.class).queryBuilder()
				.where().eq(Material.ACTIVO_FIELD_NAME, true)
				.query();
			setTipoMateriales(listaMateriales);
			
			List<MaterialTrabajo> listaMaterialesUsados;
			listaMaterialesUsados = FactoriaDAO.getDao(MaterialTrabajo.class).queryBuilder()
					.where().eq(MaterialTrabajo.ID_TRABAJO_FIELD_NAME, trabajoGestionado)
					.query();
			setMaterialesUsados(listaMaterialesUsados);
		}
		catch (SQLException e) {
			addActionError("Error al recuperar el trabajo de la Base de Datos " + e.toString());
			res = INPUT;
		}
		
		return res;
	}

	/** 
	 * Recepciona el trabajo, grabandole la fecha de entrada y la fecha estimada 
	 * de finalización
	 * @return  actionerror
	 */
	@ProporcionaFuncionalidad(identificador = FactoriaFunciones.MECANICO_GESTIONAR_TRABAJO)
	public String trabajoFacturado() {
		res = new String (SUCCESS);		

		try {
			SimpleDateFormat df = new SimpleDateFormat("dd/MM/yy"); 
			// Lo volvemos a cargar para que no se pierda
			datosTrabajoRecepcionado = FactoriaDAO.recuperar(Trabajo.class, trabajoGestionado);

			if ((df.parse(fechaRecepcionTrabajo)).after(df.parse(fechaFinTrabajo))) {
				addActionError("La fecha de finalización del trabajo no puede ser anterior a la fecha de recepcion del mismo");
				detallesSeleccion();
				res = INPUT;
			}
			else {
				datosTrabajoRecepcionado.setFechaFin(df.parse(fechaFinTrabajo));			
				datosTrabajoRecepcionado.update();
				datosTrabajoRecepcionado.refresh();
			}
		}
		catch (SQLException e) {
			addActionError("Error al hacer el update");
			detallesSeleccion();
			res = INPUT;
		}
		catch (ParseException e) {
			addActionError("Error al comprobar la fecha");
			detallesSeleccion();
			res = INPUT;
		} 
		catch (NullPointerException e) {
			addActionError("Error de puntero nulo");
			detallesSeleccion();
			res = INPUT;
		}
		return res;
	}
	
	
	/**
	 *  Modifica las horas actualmente facturadas, por si hubiera habido un error
	 * @return  actionerror
	 */
	@ProporcionaFuncionalidad(identificador = FactoriaFunciones.MECANICO_GESTIONAR_TRABAJO)
	public String modificarHorasFacturadas() {
		res = new String (SUCCESS);		

		try {
			datosTrabajoRecepcionado = FactoriaDAO.recuperar(Trabajo.class, trabajoGestionado);
			if (Integer.parseInt(horasFacturadasTrabajo.trim())<0) {
				addActionError ("El número de horas ha de ser positivo");
				detallesSeleccion();
				res = INPUT;
			}
			else {
				datosTrabajoRecepcionado.setHoras(Integer.parseInt(horasFacturadasTrabajo.trim()));
				datosTrabajoRecepcionado.update();
				datosTrabajoRecepcionado.refresh();				
			}
		} catch (SQLException e) {
			addActionError("Error al hacer el update");
			detallesSeleccion();
			res = INPUT;
		}
		catch (NumberFormatException e) {
			addActionError("Error. Las horas indicadas no son un número entero");
			detallesSeleccion();
			res = INPUT;
		} 
		catch (NullPointerException e) {
			addActionError("Error de puntero nulo");
			detallesSeleccion();
			res = INPUT;
		}
		res = detallesSeleccion();
		return res;
	}

	/** 
	 * Añade horas facturadas
	 * @return  actionerror
	 */
	@ProporcionaFuncionalidad(identificador = FactoriaFunciones.MECANICO_GESTIONAR_TRABAJO)
	public String contabilizarHoras() {
		res = new String (SUCCESS);		

		try {
			datosTrabajoRecepcionado = FactoriaDAO.recuperar(Trabajo.class, trabajoGestionado);
			if (Integer.parseInt(horasTrabajo.trim())<0) {
				addActionError ("El número de horas ha de ser positivo");
				detallesSeleccion();
				res = INPUT;
			}
			else {
				datosTrabajoRecepcionado.setHoras(Integer.parseInt(horasTrabajo.trim())+
						datosTrabajoRecepcionado.getHoras());
				datosTrabajoRecepcionado.update();
				datosTrabajoRecepcionado.refresh();				
			}
		} catch (SQLException e) {
			addActionError("Error al hacer el update");
			detallesSeleccion();
			res = INPUT;
		}
		catch (NumberFormatException e) {
			addActionError("Error. Las horas indicadas no son un número entero");
			detallesSeleccion();
			res = INPUT;
		} 
		catch (NullPointerException e) {
			addActionError("Error de puntero nulo");
			detallesSeleccion();
			res = INPUT;
		}
		res = detallesSeleccion();
		return res;
	}

	
	/** 
	 * Añade un material al trabajo. Aunque ya esté facturado el mismo material, se 
	 * añade como fila nueva
	 * @return  actionerror
	 */
	@ProporcionaFuncionalidad(identificador = FactoriaFunciones.MECANICO_GESTIONAR_TRABAJO)
	public String contabilizarMaterial() {
		res = new String (SUCCESS);		
		
		try {
			datosTrabajoRecepcionado = FactoriaDAO.recuperar(Trabajo.class, trabajoGestionado);
			if (Integer.parseInt(unidadesMaterial.trim())<0) {
				addActionError ("El número de unidades ha de ser positivo");
				detallesSeleccion();
				res = INPUT;
			}
			else {
				// Recuperamos el material de la BD
				Material datosMaterial = FactoriaDAO.recuperar(Material.class, seleccionMaterial);
				
				MaterialTrabajo mt = MaterialTrabajo.crear(datosTrabajoRecepcionado, datosMaterial, 
						Integer.parseInt(unidadesMaterial.trim()), datosMaterial.getCoste());
				mt.refresh();
			}
		} catch (SQLException e) {
			addActionError("Error al recuperar datos de la BD");
			detallesSeleccion();
			res = INPUT;
		}
		catch (NumberFormatException e) {
			addActionError("Error. Las unidades indicadas no son un número entero");
			detallesSeleccion();
			res = INPUT;
		} 
		catch (NullPointerException e) {
			addActionError("Error de puntero nulo");
			detallesSeleccion();
			res = INPUT;
		}
		res = detallesSeleccion();
		return res;
	}

	/** 
	 * Selecciona un material asignado a un trabajo para modificarlo
	 * @return  actionerror
	 */
	@ProporcionaFuncionalidad(identificador = FactoriaFunciones.MECANICO_GESTIONAR_TRABAJO)
	public String seleccionarMaterial() {
		res = new String (SUCCESS);		
		
		try {
			datosTrabajoRecepcionado = FactoriaDAO.recuperar(Trabajo.class, trabajoGestionado);
			// Recuperamos el material de la BD
			materialModificado = FactoriaDAO.recuperar(MaterialTrabajo.class, materialUsado);			
		} catch (SQLException e) {
			addActionError("Error al recuperar datos de la BD");
			detallesSeleccion();
			res = INPUT;
		}
		catch (NullPointerException e) {
			addActionError("Error de puntero nulo");
			detallesSeleccion();
			res = INPUT;
		}
		return res;
	}

	/** 
	 * Modifica el material y vuelve a la pantalla del detalle del trabajo
	 * @return  actionerror
	 */
	@ProporcionaFuncionalidad(identificador = FactoriaFunciones.MECANICO_GESTIONAR_TRABAJO)
	public String modificarMaterial(){
		res = new String (SUCCESS);		
		
		try {
			datosTrabajoRecepcionado = FactoriaDAO.recuperar(Trabajo.class, trabajoGestionado);
			materialModificado = FactoriaDAO.recuperar(MaterialTrabajo.class, materialUsado);			
			if (Integer.parseInt(unidadesMaterial.trim())<0) {
				addActionError ("El número de unidades ha de ser positivo");
				res = INPUT;
			}
			else if (Integer.parseInt(unidadesMaterial.trim())==0){
				MaterialTrabajo mt = FactoriaDAO.recuperar(MaterialTrabajo.class, materialUsado);
				mt.delete();
				mt.refresh();
				res = detallesSeleccion();
			}
			else {
				// Recuperamos el material de la BD
				MaterialTrabajo mt = FactoriaDAO.recuperar(MaterialTrabajo.class, materialUsado);
				mt.setUnidades(Integer.parseInt(unidadesMaterial.trim()));
				mt.update();
				mt.refresh();
				res = detallesSeleccion();
			}
		} catch (SQLException e) {
			addActionError("Error al recuperar datos de la BD");
			res = INPUT;
		}
		catch (NumberFormatException e) {
			addActionError("Error. Las unidades indicadas no son un número entero");
			res = INPUT;
		} 
		catch (NullPointerException e) {
			addActionError("Error de puntero nulo");
			res = INPUT;
		}
		return res;
	}
	
	@Override
	public void setSession(Map<String, Object> session) {
		userSession = session;
	}
	

}
